package cn.isjinhao.se.sql.grammar.statement.create;

import cn.isjinhao.se.sql.function.evaluator.FunctionEvaluator;
import cn.isjinhao.se.sql.grammar.CurdStatement;
import cn.isjinhao.se.sql.grammar.GrammarErrorReporterDelegate;
import cn.isjinhao.se.sql.grammar.statement.AssignmentList;
import cn.isjinhao.se.sql.grammar.statement.Grouping;
import cn.isjinhao.se.sql.grammar.statement.StatementParser;
import cn.isjinhao.se.sql.grammar.statement.create.represention.*;
import cn.isjinhao.se.sql.grammar.statement.create.visitor.SingleInsertMetaInfoDetector;
import cn.isjinhao.se.sql.grammar.statement.retrieve.SingleSelectType;
import cn.isjinhao.se.sql.lexical.scan.TokenSequence;
import cn.isjinhao.se.sql.lexical.token.Token;
import cn.isjinhao.se.sql.lexical.token.TokenType;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author ISJINHAO
 * @Date 2021/4/3 17:50
 */
public class InsertParser extends StatementParser {

    public InsertParser(TokenSequence tokenSequence, FunctionEvaluator functionEvaluator) {
        super(tokenSequence, functionEvaluator);
    }

    /**
     * insert               ->  "insert" (constrict)? "into" IDENTIFIER (((valuesRep | setRep) onDuplicateKey?) | (selectRep))
     * constrict            ->  ignore
     * valuesRep            ->  ("(" IDENTIFIER (, IDENTIFIER)* ")")? "values" ("(" binaryArithmetic (, binaryArithmetic)* ")") ("," "(" binaryArithmetic (, binaryArithmetic)* ")")*
     * insertSetRep         ->  "set" assignmentList
     * selectRep            ->  ("(" IDENTIFIER (, IDENTIFIER)* ")")? select
     * binaryArithmetic ↑   ->  unaryArithmetic (("+" | "-" | "*" | "/") unaryArithmetic)*
     * unaryArithmetic ↑    ->  ("!"|"-") unaryArithmetic | primary
     * primary ↑+           ->  #{xxx} | ? | "true" | "false" | "null" | NUMBER | STRING | IDENTIFIER | function | "(" binaryArithmetic ")"
     * function ↑           ->  functionName "(" binaryArithmetic? ("," binaryArithmetic)* ")"
     * onDuplicateKey       ->  "on" "duplicate" "key" "update" assignmentList
     * assignmentList ↑     ->  (IDENTIFIER "=" binaryArithmetic) ("," IDENTIFIER "=" binaryArithmetic)*
     */
    @Override
    public CurdStatement parse() {
        CurdStatement insert = insert();
        // 每个 Insert 语句 都需要 new 一个 SingleInsertMetaInfoDetector。
        insert.accept(new SingleInsertMetaInfoDetector());
        return insert;
    }

    private CurdStatement insert() {
        consume(TokenType.INSERT, GrammarErrorReporterDelegate.INSERT_insert_PARSE);

        Token token = tokenSequence.takeCur();
        Token constrict = null;
        if (TokenType.IGNORE.equals(token.getType())) {
            constrict = token;
            tokenSequence.advance();
        }
        consume(TokenType.INTO, GrammarErrorReporterDelegate.INSERT_insert_PARSE);

        Token tableName = tokenSequence.takeCur();
        tokenSequence.advance();

        InsertType insertType;
        CurdStatement curdStatement;
        if (tokenSequence.curEqual(TokenType.SET)) {
            curdStatement = setRep();
            insertType = InsertType.SET;
        } else {
            List<Token> tokens = columnList();
            if (tokenSequence.curEqual(TokenType.SELECT)) {
                curdStatement = selectRep(tokens);
                insertType = InsertType.SELECT;
            } else if (tokenSequence.curEqual(TokenType.VALUES)) {
                curdStatement = valuesRep(tokens);
                insertType = InsertType.VALUES;
            } else {
                error(GrammarErrorReporterDelegate.INSERT_insert_PARSE);
                return null;
            }
        }

        if (!tokenSequence.checkExistsToken()) {
            return new Insert(constrict, tableName, curdStatement);
        }

        return new Insert(constrict, tableName, curdStatement, onDuplicateKey(), insertType);
    }

    private CurdStatement selectRep(List<Token> tokens) {
        InsertSelectParser insertSelectParser = new InsertSelectParser(this.tokenSequence, getFunctionEvaluator());
        CurdStatement select = insertSelectParser.parse();
        InsertSelectRep insertSelectRep = new InsertSelectRep(tokens, select);
        saveSingleSelectContext(select, insertSelectRep, SingleSelectType.INSERT);
        return insertSelectRep;
    }

    private List<Token> columnList() {
        List<Token> tokenList = null;
        if (tokenSequence.equalThenAdvance(TokenType.LEFT_PAREN)) {
            tokenList = new ArrayList<>();
            do {
                consume(TokenType.IDENTIFIER, GrammarErrorReporterDelegate.INSERT_columnList_PARSE);
                tokenList.add(tokenSequence.takePre());
            } while (tokenSequence.equalThenAdvance(TokenType.COMMA));
            consume(TokenType.RIGHT_PAREN, GrammarErrorReporterDelegate.INSERT_columnList_PARSE);
        }
        return tokenList;
    }

    private CurdStatement onDuplicateKey() {
        consume(TokenType.ON, GrammarErrorReporterDelegate.INSERT_onDuplicateKey_PARSE);
        consume(TokenType.DUPLICATE, GrammarErrorReporterDelegate.INSERT_onDuplicateKey_PARSE);
        consume(TokenType.KEY, GrammarErrorReporterDelegate.INSERT_onDuplicateKey_PARSE);
        consume(TokenType.UPDATE, GrammarErrorReporterDelegate.INSERT_onDuplicateKey_PARSE);

        CurdStatement curdStatement = assignmentList();
        return new OnDuplicateKey(curdStatement);
    }


    /**
     * ("(" IDENTIFIER (, IDENTIFIER)* ")")? "values" ("(" binaryArithmetic (, binaryArithmetic)* ")") ("," "(" binaryArithmetic (, binaryArithmetic)* ")")*
     *
     * @param tokenList
     */
    private CurdStatement valuesRep(List<Token> tokenList) {

        consume(TokenType.VALUES, GrammarErrorReporterDelegate.INSERT_valuesRep_PARSE);

        int currentCount = -1;
        if (tokenList != null) {
            currentCount = tokenList.size();
        }
        List<List<CurdStatement>> curdStatementsList = new ArrayList<>();
        // 处理多行的值
        do {
            List<CurdStatement> curdStatementList = new ArrayList<>();
            // 处理一行的值
            consume(TokenType.LEFT_PAREN, GrammarErrorReporterDelegate.INSERT_valuesRep_PARSE);
            do {
                CurdStatement binaryArithmetic = binaryArithmetic();
                curdStatementList.add(binaryArithmetic);
            } while (tokenSequence.equalThenAdvance(TokenType.COMMA));
            consume(TokenType.RIGHT_PAREN, GrammarErrorReporterDelegate.INSERT_valuesRep_PARSE);
            if (curdStatementList.size() == currentCount || currentCount == -1) {
                currentCount = curdStatementList.size();
            } else {
                error(GrammarErrorReporterDelegate.INSERT_valuesRep_PARSE);
            }
            curdStatementsList.add(curdStatementList);
        } while (tokenSequence.equalThenAdvance(TokenType.COMMA));
        if (curdStatementsList.isEmpty()) {
            error(GrammarErrorReporterDelegate.INSERT_valuesRep_PARSE);
        }
        return new ValuesRep(tokenList, curdStatementsList);
    }

    /**
     * "set" (IDENTIFIER "=" binaryArithmetic) ("," IDENTIFIER "=" binaryArithmetic)*
     */
    private CurdStatement setRep() {
        consume(TokenType.SET, GrammarErrorReporterDelegate.INSERT_setRep_PARSE);
        CurdStatement curdStatement = assignmentList();
        if (!(curdStatement instanceof AssignmentList)) {
            error(GrammarErrorReporterDelegate.INSERT_setRep_PARSE);
        }
        AssignmentList assignmentList = (AssignmentList) curdStatement;
        if (assignmentList.getEntryList() == null || assignmentList.getEntryList().isEmpty()) {
            error(GrammarErrorReporterDelegate.INSERT_setRep_PARSE);
        }
        return new InsertSetRep(curdStatement);
    }

    /**
     * #{xxx} | ? | "true" | "false" | "null" | NUMBER | STRING | IDENTIFIER | function | "(" binaryArithmetic ")"
     */
    @Override
    protected CurdStatement primary() {
        CurdStatement primary = super.primary();
        if (primary != null) {
            return primary;
        }

        // "(" binaryArithmetic ")"
        if (tokenSequence.equalThenAdvance(TokenType.LEFT_PAREN)) {
            CurdStatement curdStatement = binaryArithmetic();
            consume(TokenType.RIGHT_PAREN, GrammarErrorReporterDelegate.INSERT_primary_PARSE);
            return new Grouping(curdStatement);
        }
        error(GrammarErrorReporterDelegate.ALL_token_undefined_PARSE);
        return null;
    }

//    private void assertValue(CurdStatement curdStatement) {
//        if(curdStatement == null) {
//        } else if(curdStatement instanceof AssignmentList) {
//            AssignmentList assignmentList = (AssignmentList) curdStatement;
//            List<AssignmentList.Entry> entryList = assignmentList.getEntryList();
//            for(AssignmentList.Entry entry : entryList) {
//                assertValue(entry.getValue());
//            }
//        } else if(curdStatement instanceof BinaryArithmetic) {
//            BinaryArithmetic binaryArithmetic = (BinaryArithmetic) curdStatement;
//            assertValue(binaryArithmetic.getLeftSelectStatement());
//            assertValue(binaryArithmetic.getRightSelectStatement());
//        } else if(curdStatement instanceof UnaryArithmetic) {
//            UnaryArithmetic unaryArithmetic = (UnaryArithmetic) curdStatement;
//            assertValue(unaryArithmetic.getSelectStatement());
//        } else if(curdStatement instanceof Identifier) {
//            error(GrammarErrorMsgDelegate.INSERT_value_CHECK);
//        }
//    }

}
